package ee.objectCloner.impl.cloners;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import ee.objectCloner.CustomCloner;
import ee.objectCloner.ObjectCloner;

public class HashMapCloner implements CustomCloner {

	@Override
	public Object clone(Object source, ObjectCloner objectCloner) {
		
		Map<?, ?> sourceMap = (Map<?, ?>) source;
		Map<Object, Object> destination = new HashMap<Object, Object>();
		
		for (Entry<?, ?> entry : sourceMap.entrySet())
		{
			Object key = entry.getKey();
			Object value = entry.getValue();
			
			destination.put(objectCloner.clone(key), objectCloner.clone(value));
		}
		
		return destination;
	}

	@Override
	/**
	 * Checks if a key from the source exists in the destination, then uses the destination 
	 * to copy, otherwise clones the source. The key is always cloned. 
	 */
	public void copy(Object source, Object destination, ObjectCloner objectCloner) {
		
		if (source.equals(destination))
		{
			return;
		}
		
		Map<?, ?> sourceMap = (Map<?, ?>) source;
		@SuppressWarnings("unchecked")
		Map<Object, Object> destinationMap = (Map<Object, Object>) destination;
		
		for (Entry<?, ?> entry : sourceMap.entrySet())
		{
			Object key = entry.getKey();
			Object destinationValue = destinationMap.get(key);
			
			key = objectCloner.clone(key);
					
			Object value = entry.getValue();
			
			if (destinationValue == null)
			{
				destinationMap.put(key, objectCloner.clone(value));
			} else
			{
				value = objectCloner.copyOrClone(value, destinationValue);
				destinationMap.put(key, value);
			}
		}
	}

	@Override
	public boolean canClone(Class<?> type) {
		return type.equals(Map.class) || type.equals(HashMap.class);
	}

	@Override
	public boolean canCopy(Class<?> sourceType, Class<?> destinationType) {
		return Map.class.isAssignableFrom(sourceType) && Map.class.isAssignableFrom(destinationType);
	}

	
}
